Skip to content

feat(mock): emit msw handlers and faker factories as separate files in split modes - #3730

Merged
melloware merged 3 commits into
orval-labs:masterfrom
ingvaldlorentzen:fix/msw-faker-split
Jul 15, 2026
Merged

feat(mock): emit msw handlers and faker factories as separate files in split modes#3730
melloware merged 3 commits into
orval-labs:masterfrom
ingvaldlorentzen:fix/msw-faker-split

Conversation

@ingvaldlorentzen

@ingvaldlorentzen ingvaldlorentzen commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

This is a follow up to the changes made in #1832

We have a situation where an API spec has very large (recursive) models, when generating MSW handlers this causes the faker "factories" to become very large, making the generated code unwieldy and causing memory issues with our builds.

There are probably many ways we could go about fixing it, but since we never use the provided faker response bodies from Orval, we would like to omit them entirely.
Generating the handlers is still very convenient, we don't have to set up all the path handling, globs etc. but we always want to provide our own response bodies (which are generally custom made faker objects to more accurately reflect production-like data).

Anyways, this PR aims to provide this feature. If you set operationResponses to false in the msw config, the handlers will be generated without any faker responses.

When operationResponses is set to true (default) and both msw and faker is configured in the mock config, the faker objects/factories, are now imported from the faker files (in split mode obviously) instead of being re-defined within the msw handlers.

The other modes should behave the same as before, with the faker factories inlined with msw handlers when only msw is defined and operationResponses: true (default). And of course with only faker defined, only the faker files are generated.

There are _a lot _ of changes in this PR, but they are mostly sample and snapshot changes, since imports etc. change.

I'm new to contributing to Orval, and am not very familiar with the codebase. So I may very well have made mistakes here. I'll ofc gladly make any requested tweaks if wanted!

Summary by CodeRabbit

  • New Features
    • Added an operationResponses option to control whether MSW outputs include per-operation response mock factories.
    • Enhanced split and tags-split generation to properly move and share Faker response factories across files, avoiding duplication.
    • Improved import generation by grouping and deduplicating entries coming from the same source.
  • Bug Fixes
    • When operationResponses is disabled, generated handler-only MSW output now omits per-operation response factories and correctly adjusts fallback behavior.
  • Documentation
    • Updated the configuration reference for operationResponses, including expected cross-file behavior.
  • Tests
    • Expanded coverage for the new output/cross-file wiring and updated affected scenarios.

…n split modes

In split and tags-split modes the get<Op>ResponseMock factories were
duplicated in <name>.msw.ts and <name>.faker.ts. Now the faker file is
the single home of the factories and the msw file imports and re-exports
them. MSW-only output is unchanged unless the new operationResponses:
false flag is set on the msw generator entry (handlers only, undefined
fallbacks).
Copilot AI review requested due to automatic review settings July 15, 2026 12:32
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

MSW and Faker mock generation now preserves full outputs, moves response factories into Faker files for split modes, and adds MSW imports and re-exports. A new operationResponses option supports handler-only output, with updated writer logic, tests, samples, configuration, and documentation.

Changes

MSW/Faker output wiring

Layer / File(s) Summary
Full mock output model and transformations
packages/core/src/types.ts, packages/core/src/writers/mock-outputs.ts, packages/core/src/writers/target.ts, packages/core/src/writers/target-tags.ts, packages/core/src/writers/mock-outputs.test.ts
Targets retain full mock outputs while shared helpers flatten, collapse, import, and re-export MSW/Faker response factories.
Split writer integration
packages/core/src/writers/split-mode.ts, packages/core/src/writers/split-tags-mode.ts, packages/core/src/writers/generate-imports-for-builder.ts, packages/core/src/writers/*test.ts
Split and tags-split writers move factories to Faker files, add grouped cross-file imports, and preserve MSW re-exports.
Generated output validation
samples/*/src/api/endpoints/*.msw.ts, tests/api-generation.spec.ts
Generated MSW samples and API assertions verify response factories are imported from and re-exported through companion Faker files.
Configuration and documentation
tests/configs/mock.config.ts, docs/content/docs/reference/configuration/output.mdx
Documents and configures operationResponses, including handler-only split and tags-split output.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SplitWriter
  participant MockOutputHelpers
  participant ImportBuilder
  participant FakerFile
  participant MswFile
  SplitWriter->>MockOutputHelpers: collapse and flatten full outputs
  MockOutputHelpers->>ImportBuilder: provide cross-file Faker imports
  ImportBuilder->>MswFile: emit grouped imports
  MswFile->>FakerFile: import response factories
  MswFile->>MswFile: emit Faker re-exports
Loading

Possibly related PRs

Suggested labels: mock, msw

Suggested reviewers: jakiestfu, aqeelat, melloware

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: split-mode MSW handlers and Faker factories are emitted into separate files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/core/src/writers/generate-imports-for-builder.ts (1)

137-156: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a delimiter to the uniqueBy key to prevent collisions.

When concatenating name and importPath, it is safer to use a delimiter like | to avoid edge-case collisions (e.g., name: 'Foo', importPath: '/bar' vs name: 'Fo', importPath: 'o/bar'). This also matches the pattern used elsewhere in the file.

♻️ Proposed fix
   const otherImportsMap = new Map<string, GeneratorImport[]>();
   for (const imp of uniqueBy(
     imports.filter(
       (i): i is GeneratorImport & { importPath: string } => !!i.importPath,
     ),
-    (x) => x.name + x.importPath,
+    (x) => `${x.name}|${x.importPath}`,
   )) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/generate-imports-for-builder.ts` around lines 137 -
156, Update the uniqueBy key in the otherImportsMap construction to concatenate
imp.name and imp.importPath with a delimiter, matching the file’s existing
key-building pattern and preventing ambiguous combinations from colliding.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/reference/configuration/output.mdx`:
- Around line 635-636: Update the split and tags-split mode documentation to
qualify the Faker placement behavior: response factories move to the .faker file
only when the Faker output declares every factory referenced by MSW. Document
that otherwise factories remain in .msw, or become undefined when
operationResponses is false.

In `@packages/core/src/writers/mock-outputs.ts`:
- Around line 87-99: Prioritize the operationResponses-disabled rewrite in
mockOutputs before the fakerDeclaresAllReferenced branch so MSW handlers omit
response bodies even when Faker factories are moved; update
packages/core/src/writers/mock-outputs.ts lines 87-99 accordingly, and update
packages/core/src/writers/mock-outputs.test.ts lines 218-228 to expect undefined
instead of getPetResponseMock().
- Around line 154-164: Preserve runtime extensions for cross-file faker imports
generated by buildCrossFileFakerImports: update its call sites in
packages/core/src/writers/split-mode.ts (not shown) and
packages/core/src/writers/split-tags-mode.ts:387-394 to pass
getImportExtension(extension, output.tsconfig), or update
buildCrossFileFakerImports in packages/core/src/writers/mock-outputs.ts:154-164
to apply that extension before generateImportsForBuilder emits importPath.
Ensure Node16/NodeNext output uses paths such as ./pets.faker.js while other
modes retain their existing behavior.

---

Nitpick comments:
In `@packages/core/src/writers/generate-imports-for-builder.ts`:
- Around line 137-156: Update the uniqueBy key in the otherImportsMap
construction to concatenate imp.name and imp.importPath with a delimiter,
matching the file’s existing key-building pattern and preventing ambiguous
combinations from colliding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c31d89b0-8d5d-4167-bf48-a43797cc60c8

📥 Commits

Reviewing files that changed from the base of the PR and between 3112104 and 126a863.

⛔ Files ignored due to path filters (113)
  • samples/hono/hono-with-fetch-client/__snapshots__/next-app/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/hono/hono-with-fetch-client/next-app/app/gen/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/next-app-with-fetch/__snapshots__/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/next-app-with-fetch/app/gen/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/react-app-with-swr/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.msw.ts is excluded by !**/__snapshots__/**
  • samples/react-app-with-swr/fetch-client/__snapshots__/endpoints/swaggerPetstore.msw.ts is excluded by !**/__snapshots__/**
  • samples/react-app/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.msw.ts is excluded by !**/__snapshots__/**
  • samples/react-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.msw.ts is excluded by !**/__snapshots__/**
  • samples/react-query/custom-fetch/__snapshots__/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/react-query/custom-fetch/src/gen/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/svelte-query/basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.msw.ts is excluded by !**/__snapshots__/**
  • samples/svelte-query/custom-fetch/__snapshots__/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/svelte-query/custom-fetch/src/gen/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/svelte-query/v6-custom-fetch/__snapshots__/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/svelte-query/v6-custom-fetch/src/gen/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/swr-with-effect/__snapshots__/endpoints/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/swr-with-effect/src/gen/endpoints/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/swr-with-zod/__snapshots__/endpoints/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/swr-with-zod/src/gen/endpoints/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/vue-query/custom-fetch/__snapshots__/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • samples/vue-query/custom-fetch/src/gen/pets/pets.msw.ts is excluded by !**/gen/**
  • samples/vue-query/vue-query-basic/__snapshots__/endpoints/petstoreFromFileSpecWithTransformer.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/angular/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/angular/tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/angular/tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-mutator/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-mutator/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/tags-split-shared-models/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/tags-split-shared-models/stores/stores.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/default/index-mock-file-split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/default/issue-2206-msw-info-typing/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/default/issue-2998/documents/documents.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/default/issue-2998/requests/requests.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/default/override-mock/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/fetch/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/fetch/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/fetch/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-fetch/pets/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split-multi-fetch/store/store/store.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/issue-3574-strict-mock-tags-split/pets/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-mock-path/mocks/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/endpoints.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/cat.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/catType.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/createPetsBody.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/createPetsParams.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/createPetsSort.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/dachshund.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/dachshundBreed.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/dog.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/dogType.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/error.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/index.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/labradoodle.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/labradoodleBreed.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/listPetsParams.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/listPetsSort.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/pet.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/petCallingCode.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/petCountry.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/petWithTag.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split-msw-handlers-only/model/pets.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-mock-path/mocks/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-mock-path/mocks/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/health/health.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/cat.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/catType.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/createPetsBody.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/createPetsParams.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/createPetsSort.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/dachshund.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/dachshundBreed.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/dog.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/dogType.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/error.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/index.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/labradoodle.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/labradoodleBreed.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/listPetsParams.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/listPetsSort.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/pet.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/petCallingCode.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/petCountry.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/petWithTag.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/model/pets.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-msw-handlers-only/pets/pets.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-per-generator-path/msw/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/mock/tags-split-per-generator-path/msw/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/react-query/importFromSubdirectory/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/react-query/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/react-query/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/react-query/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/svelte-query/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/svelte-query/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/svelte-query/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/swr/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/swr/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/swr/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/vue-query/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/vue-query/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/vue-query/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/zod/petstore-tags-split/health/health.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/zod/petstore-tags-split/pets/pets.msw.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/zod/split/endpoints.msw.ts is excluded by !**/__snapshots__/**
📒 Files selected for processing (19)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/types.ts
  • packages/core/src/writers/generate-imports-for-builder.test.ts
  • packages/core/src/writers/generate-imports-for-builder.ts
  • packages/core/src/writers/mock-outputs.test.ts
  • packages/core/src/writers/mock-outputs.ts
  • packages/core/src/writers/split-mode.ts
  • packages/core/src/writers/split-tags-mode.ts
  • packages/core/src/writers/target-tags.ts
  • packages/core/src/writers/target.ts
  • samples/react-app-with-swr/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts
  • samples/react-app-with-swr/fetch-client/src/api/endpoints/swaggerPetstore.msw.ts
  • samples/react-app/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts
  • samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts
  • samples/solid-start/basic/src/api/endpoints/petstore.msw.ts
  • samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts
  • samples/vue-query/vue-query-basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts
  • tests/api-generation.spec.ts
  • tests/configs/mock.config.ts

Comment thread docs/content/docs/reference/configuration/output.mdx Outdated
Comment on lines +87 to +99
if (fakerDeclaresAllReferenced) {
return mockOutputs.map((m) =>
m.type === OutputMockType.MSW
? { ...m, implementation: { ...m.implementation, function: '' } }
: m,
);
}

if (options.mswOperationResponses === false) {
const strippedHandler = mswEntry.implementation.handler.replaceAll(
RESPONSE_MOCK_CALL_RE,
': undefined',
);

@coderabbitai coderabbitai Bot Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor operationResponses: false before moving Faker factories.

When Faker declares the referenced factories, the first branch keeps the fallback calls, so handler-only mode still returns generated response bodies.

  • packages/core/src/writers/mock-outputs.ts#L87-L99: apply the mswOperationResponses === false rewrite before the Faker-movement branch.
  • packages/core/src/writers/mock-outputs.test.ts#L218-L228: expect the fallback to become undefined, not getPetResponseMock().

This contradicts the PR objective that operationResponses: false omits response bodies.

📍 Affects 2 files
  • packages/core/src/writers/mock-outputs.ts#L87-L99 (this comment)
  • packages/core/src/writers/mock-outputs.test.ts#L218-L228
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/mock-outputs.ts` around lines 87 - 99, Prioritize
the operationResponses-disabled rewrite in mockOutputs before the
fakerDeclaresAllReferenced branch so MSW handlers omit response bodies even when
Faker factories are moved; update packages/core/src/writers/mock-outputs.ts
lines 87-99 accordingly, and update
packages/core/src/writers/mock-outputs.test.ts lines 218-228 to expect undefined
instead of getPetResponseMock().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to tweaking this, but this is working as intended right now. The idea is that operationResponses on the msw config controls if the msw files are supposed to include the factories. If faker is also configured that takes "precedence", and the responses are imported.

The responses can always be overridden, so I think this is the right choice, but again, ofc open to tweaking this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ingvaldlorentzen, thanks for the clarification — that makes sense as a design choice. Since operationResponses on the MSW config is scoped to whether MSW itself defines the factories (not whether responses exist at all), letting Faker's factories take precedence when both are configured is a reasonable and consistent behavior, especially since callers can always override the response via overrideResponse. I'll withdraw this concern.

(=^・ω・^=) ノ understood, noted for next time


✏️ Learnings added
Learnt from: ingvaldlorentzen
Repo: orval-labs/orval PR: 3730
File: packages/core/src/writers/mock-outputs.ts:88-100
Timestamp: 2026-07-15T13:21:13.658Z
Learning: In `packages/core/src/writers/mock-outputs.ts`, `collapseMswFakerFullOutputs` intentionally gives Faker precedence over the MSW `operationResponses: false` setting: if a Faker output declares all `get<Op>ResponseMock` factories referenced by MSW handlers, those factories are moved/imported from Faker regardless of `mswOperationResponses`. The `mswOperationResponses === false` fallback-to-`undefined` rewrite only applies when Faker does not declare all referenced factories (e.g., Faker isn't configured, or Faker also has `operationResponses: false`). This is intended: the MSW `operationResponses` flag only controls whether MSW itself embeds the factories, not whether responses exist when Faker is available, since responses can always be overridden by callers.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread packages/core/src/writers/mock-outputs.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new MSW mock configuration option to avoid generating (and in split modes, duplicating) potentially huge faker response factories, while preserving existing import ergonomics in split outputs.

Changes:

  • Introduces operationResponses on the MSW mock generator config to allow “handlers only” output (fallback response becomes undefined) in split / tags-split.
  • In split / tags-split when both MSW + Faker are configured, moves get<Op>ResponseMock* factories into the .faker file and has .msw files import + re-export them (so prior import paths keep working).
  • Updates tests, snapshots, and samples to reflect the new split output structure (imports/re-exports instead of inline factory declarations).

Reviewed changes

Copilot reviewed 132 out of 132 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/configs/mock.config.ts Adds new split and tags-split configs exercising MSW “handlers-only” mode.
tests/api-generation.spec.ts Updates assertions for “factories live in faker file” behavior (import + re-export from msw).
packages/core/src/writers/target.ts Exposes mockOutputsFull so split writers can post-process function vs handler portions before flattening.
packages/core/src/writers/target-tags.ts Same as above for tag-bucket targets; uses shared flattenMockOutput.
packages/core/src/writers/split-mode.ts Collapses MSW/Faker outputs: either strip factories + import from faker, or strip factories + replace fallback with undefined when operationResponses: false.
packages/core/src/writers/split-tags-mode.ts Same logic as split-mode, applied per-tag; adds cross-file faker imports + re-exports in .msw outputs.
packages/core/src/writers/mock-outputs.ts New helpers for: flattening full outputs, deciding MSW/Faker factory placement, extracting referenced response-mock names, and building cross-file imports/re-exports.
packages/core/src/writers/generate-imports-for-builder.ts Groups explicit importPath imports by dependency so multiple cross-file imports become a single import statement.
packages/core/src/writers/generate-imports-for-builder.test.ts Adds coverage for grouping/deduping importPath imports.
packages/core/src/types.ts Adds operationResponses?: boolean to MswMockOptions.
docs/content/docs/reference/configuration/output.mdx Documents MSW operationResponses and the MSW+Faker split-mode import/re-export behavior.
tests/snapshots/zod/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/vue-query/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/swr/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/svelte-query/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/react-query/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/react-query/importFromSubdirectory/endpoints.msw.ts Snapshot update: msw imports + re-exports endpoint factories from .faker file.
tests/snapshots/mock/tags-split-per-generator-path/msw/health/health.msw.ts Snapshot update: per-generator-path msw imports + re-exports from faker path.
tests/snapshots/mock/tags-split-msw-handlers-only/pets/pets.ts New snapshot: generated client output for tags-split handlers-only fixture.
tests/snapshots/mock/tags-split-msw-handlers-only/pets/pets.msw.ts New snapshot: tags-split MSW handlers-only output (fallback undefined).
tests/snapshots/mock/tags-split-msw-handlers-only/health/health.ts New snapshot: generated client output for tags-split handlers-only fixture.
tests/snapshots/mock/tags-split-msw-handlers-only/health/health.msw.ts New snapshot: text handler uses resolved override or undefined and returns text.
tests/snapshots/mock/tags-split-msw-handlers-only/model/* New snapshots: model outputs for handlers-only fixture.
tests/snapshots/mock/tags-split-mock-path/mocks/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/mock/split-msw-handlers-only/endpoints.ts New snapshot: split handlers-only fixture client output.
tests/snapshots/mock/split-msw-handlers-only/endpoints.msw.ts New snapshot: split handlers-only MSW output (fallback undefined).
tests/snapshots/mock/split-msw-handlers-only/model/* New snapshots: model outputs for split handlers-only fixture.
tests/snapshots/mock/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/mock/issue-3574-strict-mock-tags-split/pets/pets/pets.msw.ts Snapshot update: strict-mock msw imports + re-exports response factories from .faker file; removes inline factories.
tests/snapshots/mock/issue-3574-strict-mock-tags-split-multi-fetch/store/store/store.msw.ts Snapshot update: msw imports + re-exports response factories from .faker file; removes inline factories.
tests/snapshots/mock/issue-3574-strict-mock-tags-split-fetch/pets/pets/pets.msw.ts Snapshot update: msw imports + re-exports response factories from .faker file; removes inline factories.
tests/snapshots/fetch/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/default/override-mock/endpoints.msw.ts Snapshot update: msw imports + re-exports endpoint factories from .faker file instead of inlining.
tests/snapshots/default/issue-2998/requests/requests.msw.ts Snapshot update: msw imports + re-exports request factories from .faker file.
tests/snapshots/default/issue-2998/documents/documents.msw.ts Snapshot update: msw imports + re-exports document factories from .faker file.
tests/snapshots/default/issue-2206-msw-info-typing/endpoints.msw.ts Snapshot update: msw imports + re-exports response factory from .faker file.
tests/snapshots/axios/tags-split-shared-models/stores/stores.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/axios/tags-split-shared-models/pets/pets.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/axios/petstore-tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/axios/petstore-tags-split-mutator/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
tests/snapshots/angular/tags-split/health/health.msw.ts Snapshot update: msw imports + re-exports factories from .faker file.
samples/vue-query/vue-query-basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/vue-query/vue-query-basic/snapshots/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/vue-query/custom-fetch/src/gen/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/vue-query/custom-fetch/snapshots/pets/pets.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/swr-with-zod/src/gen/endpoints/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/swr-with-effect/src/gen/endpoints/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/swr-with-effect/snapshots/endpoints/pets/pets.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/svelte-query/v6-custom-fetch/src/gen/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/svelte-query/custom-fetch/src/gen/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/svelte-query/custom-fetch/snapshots/pets/pets.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/svelte-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/svelte-query/basic/snapshots/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/solid-start/basic/src/api/endpoints/petstore.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/react-query/custom-fetch/src/gen/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/react-query/custom-fetch/snapshots/pets/pets.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/react-query/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/react-query/basic/snapshots/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/react-app/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/react-app/snapshots/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/react-app-with-swr/fetch-client/src/api/endpoints/swaggerPetstore.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/react-app-with-swr/fetch-client/snapshots/endpoints/swaggerPetstore.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/react-app-with-swr/basic/src/api/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/react-app-with-swr/basic/snapshots/endpoints/petstoreFromFileSpecWithTransformer.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/next-app-with-fetch/app/gen/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/next-app-with-fetch/snapshots/pets/pets.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.
samples/hono/hono-with-fetch-client/next-app/app/gen/pets/pets.msw.ts Sample update: msw imports + re-exports factories from .faker file.
samples/hono/hono-with-fetch-client/snapshots/next-app/pets/pets.msw.ts Snapshot update for sample: msw imports + re-exports factories from .faker file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/core/src/writers/generate-imports-for-builder.ts (1)

142-142: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include alias and import type in the deduplication key.

Currently, deduplicating otherImports by just name and importPath will incorrectly merge imports that share these attributes but differ in alias, values (e.g., type vs. value imports), or default.

Consider aligning this with how schemaImports are deduplicated by including those fields in the key to prevent unintended collisions.

♻️ Proposed refactor
-    (x) => `${x.name}|${x.importPath}`,
+    (x) => `${x.name}|${x.alias ?? ''}|${String(x.values)}|${String(x.default)}|${x.importPath}`,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/writers/generate-imports-for-builder.ts` at line 142,
Update the otherImports deduplication key in the surrounding import-generation
logic to include alias, values/import type, and default alongside name and
importPath, matching the schemaImports key structure so distinct imports are not
merged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/src/writers/generate-imports-for-builder.ts`:
- Line 142: Update the otherImports deduplication key in the surrounding
import-generation logic to include alias, values/import type, and default
alongside name and importPath, matching the schemaImports key structure so
distinct imports are not merged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 530897c2-3c84-437d-ae65-cdac4b415c81

📥 Commits

Reviewing files that changed from the base of the PR and between 126a863 and 4e04eb1.

📒 Files selected for processing (6)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/writers/generate-imports-for-builder.ts
  • packages/core/src/writers/mock-outputs.test.ts
  • packages/core/src/writers/mock-outputs.ts
  • packages/core/src/writers/split-mode.ts
  • packages/core/src/writers/split-tags-mode.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • docs/content/docs/reference/configuration/output.mdx
  • packages/core/src/writers/split-mode.ts
  • packages/core/src/writers/mock-outputs.test.ts
  • packages/core/src/writers/split-tags-mode.ts

@melloware

Copy link
Copy Markdown
Collaborator

cc @jakiestfu

@jakiestfu

Copy link
Copy Markdown
Contributor

@ingvaldlorentzen would you mind showing me a sample configuration that disables faker but leaves MSW?

@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

@orval/angular

bun add https://pkg.pr.new/@orval/angular@4e04eb1

@orval/axios

bun add https://pkg.pr.new/@orval/axios@4e04eb1

@orval/core

bun add https://pkg.pr.new/@orval/core@4e04eb1

@orval/effect

bun add https://pkg.pr.new/@orval/effect@4e04eb1

@orval/fetch

bun add https://pkg.pr.new/@orval/fetch@4e04eb1

@orval/hono

bun add https://pkg.pr.new/@orval/hono@4e04eb1

@orval/mcp

bun add https://pkg.pr.new/@orval/mcp@4e04eb1

@orval/mock

bun add https://pkg.pr.new/@orval/mock@4e04eb1

orval

bun add https://pkg.pr.new/orval@4e04eb1

@orval/query

bun add https://pkg.pr.new/@orval/query@4e04eb1

@orval/solid-start

bun add https://pkg.pr.new/@orval/solid-start@4e04eb1

@orval/swr

bun add https://pkg.pr.new/@orval/swr@4e04eb1

@orval/zod

bun add https://pkg.pr.new/@orval/zod@4e04eb1

commit: 4e04eb1

@ingvaldlorentzen

Copy link
Copy Markdown
Contributor Author

@ingvaldlorentzen would you mind showing me a sample configuration that disables faker but leaves MSW?

Yeah, all you need is to set operationResponses: false on a mock config without faker defined. Like this:

  mock: {
    generators: [
      {
        operationResponses: false,
        type: OutputMockType.MSW,
      },
    ],
  },

@ingvaldlorentzen

ingvaldlorentzen commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

@jakiestfu just to clarify, if you define both msw and faker, the faker bodies are no longer inlined in the .msw file instead they are imported from the .faker file, reducing duplication from the current way it works.

However, they are re-exported from the .msw file, this is to avoid breaking existing code, as many might be importing these functions from the .msw files.
So in this PR you can import get<op>ResponseMock functions from both .faker and .msw files, but they are only defined in the .faker files.

Hope that makes sense.

Config e.g. with both:

  mock: {
    generators: [
      {
        operationResponses: false, // Has no effect in this case
        type: OutputMockType.MSW,
      },
      {
        type: OutputMockType.FAKER,
      },
    ],
  },

Edit:
In this case, where you've defined both msw and faker, the operationResponses: false, on msw doesn't actually do anything, since you're basically requesting to generate these via faker. This was something coderabbit flagged as confusing, personally I think it makes sense, but very open to tweaking the config behaviour here if you have any suggestions.

@melloware

Copy link
Copy Markdown
Collaborator

this LGTM

@melloware melloware added mock Related to mock generation msw MSW related issues labels Jul 15, 2026
@melloware melloware added this to the 8.23.0 milestone Jul 15, 2026
@melloware
melloware merged commit 68a0ea2 into orval-labs:master Jul 15, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mock Related to mock generation msw MSW related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants